home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume24 / newsgate / part02 < prev    next >
Encoding:
Internet Message Format  |  1991-10-09  |  53.4 KB

  1. Subject:  v24i052:  News/mail gateway package, Part02/04
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4. X-Checksum-Snefru: f0b53f6c 640ad9a2 bb6caa7a 10f9b3ef
  5.  
  6. Submitted-by: Rich $alz <rsalz@bbn.com>
  7. Posting-number: Volume 24, Issue 52
  8. Archive-name: newsgate/part02
  9.  
  10. #! /bin/sh
  11. # This is a shell archive.  Remove anything before this line, then feed it
  12. # into a shell via "sh file" or similar.  To overwrite existing files,
  13. # type "sh file -c".
  14. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  15. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  16. # Contents:  mail2news.c mkmailpost.sh news2mail.c regex.3 rfc822.c
  17. # Wrapped by rsalz@litchi.bbn.com on Fri Mar 15 16:42:26 1991
  18. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  19. echo If this archive is complete, you will see the following message:
  20. echo '          "shar: End of archive 2 (of 4)."'
  21. if test -f 'mail2news.c' -a "${1}" != "-c" ; then 
  22.   echo shar: Will not clobber existing file \"'mail2news.c'\"
  23. else
  24.   echo shar: Extracting \"'mail2news.c'\" \(14113 characters\)
  25.   sed "s/^X//" >'mail2news.c' <<'END_OF_FILE'
  26. X/*
  27. X**  MAIL2NEWS
  28. X**  Gateway mail messages into netnews.  Usage:
  29. X**    mail2news [inews flags] -o Organization
  30. X**  In order to do this, there are a number of interesting transformations
  31. X**  that need to be made on the headers...
  32. X**
  33. X**  This program is descended from:  @(#)recnews.c 2.10 4/16/85.
  34. X*/
  35. X#include "gate.h"
  36. X#include <signal.h>
  37. X#include <sys/file.h>
  38. X#ifdef    RCSID
  39. Xstatic char RCS[] =
  40. X    "$Header: /nfs/papaya/u2/rsalz/src/newsgate/src/RCS/mail2news.c,v 1.13 91/03/13 15:57:32 rsalz Exp $";
  41. X#endif    /* RCSID */
  42. X
  43. X
  44. X#define    WAIT_CORED(s)        (s & 0200)
  45. X#define    WAIT_EXITSIG(s)        (s & 0177)
  46. X#define    WAIT_EXITCODE(s)    ((s >> 8) & 0377)
  47. X
  48. X/* For those who don't have this in <sys/file.h>. */
  49. X#ifndef    R_OK
  50. X#define    R_OK            4
  51. X#endif    /* R_OK */
  52. X
  53. X/* Stuff for pipe(2). */
  54. X#define STDIN            0
  55. X#define    PIPE_READER        0
  56. X#define    PIPE_WRITER        1
  57. X
  58. X
  59. X/* Global variables. */
  60. XSTATIC int    Debugging;
  61. XSTATIC int    ChildPid = -1;
  62. Xchar        *Pname;
  63. X
  64. X
  65. X
  66. X/*
  67. X**  Go down to the absolute minimum.
  68. X*/
  69. XSTATIC void
  70. XTrimEnvironment()
  71. X{
  72. X    environ = NULL;
  73. X}
  74. X
  75. X
  76. X/*
  77. X**  Quickie hack to see of a mail message is a "please drop me" request.
  78. X**  Reads the message on the input file, and returns NULL if it should
  79. X**  be ignored, or a FILE handle if we should process it.
  80. X**
  81. X**  The original stand-alone program written was Russ Nelson,
  82. X**  <nelson@clutx.clarkson.edu>.  I hacked on it, and made it into a
  83. X**  subroutine.  Perhaps a better way to test is to make the test less
  84. X**  conservative, and see what "real" articles get caught, and make
  85. X**  adjustments then?  Comments solicited.
  86. X*/
  87. XSTATIC FILE *
  88. XIsSubRequest(F)
  89. X    register FILE    *F;
  90. X{
  91. X    register FILE    *Out;
  92. X    register char    *p;
  93. X    register int    c;
  94. X    register int    drop_or_add;
  95. X    register int    from_or_to;
  96. X    register int    mail_word;
  97. X    register int    count;
  98. X    char        word[SM_SIZE];
  99. X    char        buff[SM_SIZE];
  100. X
  101. X    /* Create temp file; if we can't let the message through. */
  102. X    if ((Out = fopen(mktemp(strcpy(buff, TEMPFILE)), "w")) == NULL)
  103. X    return F;
  104. X
  105. X    /* Clear counts. */
  106. X    drop_or_add = 0;
  107. X    from_or_to = 0;
  108. X    mail_word = 0;
  109. X    count = 0;
  110. X
  111. X    /* Read input a word at a time. */
  112. X    for (p = word; (c = getc(F)) != EOF; ) {
  113. X    (void)putc(c, Out);
  114. X    if (!isalpha(c)) {
  115. X        *p = '\0';
  116. X        if (p > word)
  117. X        count++;
  118. X        p = word;
  119. X
  120. X        if (EQ(word, "remove") || EQ(word, "drop") || EQ(word, "off")
  121. X         || EQ(word, "subscribe") || EQ(word, "get") || EQ(word, "add"))
  122. X        drop_or_add++;
  123. X        else if (EQ(word, "from") || EQ(word, "to"))
  124. X        from_or_to++;
  125. X        else if (EQ(word, "mail") || EQ(word, "mailing")
  126. X          || EQ(word, "list") || EQ(word, "dl"))
  127. X        mail_word++;
  128. X    }
  129. X    else if (p < &word[sizeof word - 1])
  130. X        *p++ = isupper(c) ? tolower(c) : c;
  131. X    }
  132. X
  133. X    (void)fclose(F);
  134. X    (void)fclose(Out);
  135. X
  136. X    /* Use fancy-shmancy AI techniques to determine what the message is. */
  137. X    c = count < 25 && drop_or_add && from_or_to && mail_word;
  138. X    F = c ? NULL : fopen(buff, "r");
  139. X
  140. X    (void)unlink(buff);
  141. X    return F;
  142. X}
  143. X
  144. X
  145. X/*
  146. X**  Modify the Newsgroups: as directed by the command string.
  147. X*/
  148. XSTATIC void
  149. XDoCommand(hp, command, group)
  150. X    register HBUF        *hp;
  151. X    char            *command;
  152. X    char            *group;
  153. X{
  154. X    register char        *p;
  155. X    register int        i;
  156. X    register int        n;
  157. X    register int        nng;
  158. X    char            **tokens;
  159. X    char            **ng;
  160. X    char            buff[BUFSIZ];
  161. X
  162. X    if ((n = Split(command, &tokens, '\0')) == 0) {
  163. X    SplitFree(&tokens);
  164. X    return;
  165. X    }
  166. X
  167. X    nng = Split(hp->nbuf, &ng, NGDELIM);
  168. X    p = hp->nbuf;
  169. X    switch (tokens[0][0]) {
  170. X    case 'a':                /* Add        */
  171. X    if (n > 1)
  172. X        for (p += strlen(p), i = 1; i < n; i++) {
  173. X        *p++ = NGDELIM;
  174. X        p += APPEND(p, tokens[i]);
  175. X        }
  176. X    break;
  177. X    case 'd':                /* Delete    */
  178. X    for (i = 0; i < nng; i++)
  179. X        if (!EQ(ng[i], group)) {
  180. X        if (p > hp->nbuf)
  181. X            *p++ = NGDELIM;
  182. X        p += APPEND(p, ng[i]);
  183. X        }
  184. X    if (p == hp->nbuf)
  185. X        Strcpy(hp->nbuf, "junk");
  186. X    break;
  187. X    case 'k':                /* Kill        */
  188. X    Fprintf(stderr, "%s:  Your posting to %s was killed by %s.\n",
  189. X        Pname, hp->nbuf, n > 1 ? tokens[1] : group);
  190. X    exit(EX_NOPERM);
  191. X    /* NOTREACHED */
  192. X    case 'm':                /* Move        */
  193. X    if (n > 1)
  194. X        if (nng == 1)
  195. X        Strcpy(hp->nbuf, tokens[1]);
  196. X        else
  197. X        for (i = 0; i < nng; i++) {
  198. X            if (p > hp->nbuf)
  199. X            *p++ = NGDELIM;
  200. X            p += APPEND(p, EQ(ng[i], group) ? tokens[1] : ng[i]);
  201. X        }
  202. X    break;
  203. X    case 'q':                /* Quiet kill    */
  204. X    if (Debugging) {
  205. X        (void)printf("Quiet kill (ignored for debugging).\n");
  206. X        break;
  207. X    }
  208. X    /* Eat the message up, and pretend we delivered it. */
  209. X    while (fgets(buff, sizeof buff, stdin))
  210. X        ;
  211. X    exit(EX_OK);
  212. X    /* NOTREACHED */
  213. X    }
  214. X
  215. X    SplitFree(&tokens);
  216. X    SplitFree(&ng);
  217. X}
  218. X
  219. X
  220. X/*
  221. X**  Split a line that looks like XpatternXcommandX into the pattern and
  222. X**  the command.  Initialize the RE matcher with the pattern, and return
  223. X**  the command.
  224. X*/
  225. XSTATIC char *
  226. XParsePattern(p, lineno)
  227. X    register char    *p;
  228. X    int            lineno;
  229. X{
  230. X    register char    *cp;
  231. X    register char    *command;
  232. X    register char    delim;
  233. X    char        *RE;
  234. X
  235. X    /* Ignore comments and blank lines. */
  236. X    if (*p == '#' || *p == '\0')
  237. X    return NULL;
  238. X
  239. X    for (delim = *p++, RE = cp = p, command = NULL; *cp; *p++ = *cp++)
  240. X    if (*cp == '\\' && cp[1] == delim)
  241. X        cp++;
  242. X    else if (*cp == delim) {
  243. X        /* Found delimiter; mark command, terminate RE. */
  244. X        command = ++cp;
  245. X        *p = '\0';
  246. X        break;
  247. X    }
  248. X
  249. X    if (command == NULL || *command == '\0')
  250. X    Fprintf(stderr, "%s:  Incomplete regular expression, line %d.\n",
  251. X        Pname, lineno);
  252. X    else if (cp = re_comp(RE))
  253. X    Fprintf(stderr, "%s:  Bad regular expression, line %d: %s.\n",
  254. X        Pname, lineno, cp);
  255. X    else
  256. X    return command;
  257. X
  258. X#ifdef    lint
  259. X    /* My, my, aren't we anal. */
  260. X    (void)re_subs("", "");
  261. X    re_modw("");
  262. X#endif    /* lint */
  263. X
  264. X    return NULL;
  265. X}
  266. X
  267. X
  268. X/*
  269. X**  Convert string to lower case, return a new copy.
  270. X*/
  271. XSTATIC char *
  272. XMakeLowerCopy(s)
  273. X    register char    *s;
  274. X{
  275. X    register char    *p;
  276. X
  277. X    for (p = s = COPY(s); *p; p++)
  278. X    if (isupper(*p))
  279. X        *p = tolower(*p);
  280. X    return s;
  281. X}
  282. X
  283. X
  284. X/*
  285. X**  Change newsgroups if the Subject:, Keywords:, or Summary: match a
  286. X**  pattern found in the newsgroup remap file.
  287. X*/
  288. XSTATIC void
  289. XEditnewsgroups(hp)
  290. X    register HBUF        *hp;
  291. X{
  292. X    register char        *p;
  293. X    register int        n;
  294. X    register int        i;
  295. X    register int        j;
  296. X    register int        t;
  297. X    char            **groups;
  298. X    char            **mapline;
  299. X    char            *hdrline[4];
  300. X    char            buff[LG_SIZE];
  301. X
  302. X    /* Copy some headers, but if nothing's there, give up. */
  303. X    i = 0;
  304. X    if (hp->title)
  305. X    hdrline[i++] = MakeLowerCopy(hp->title);
  306. X    if (hp->keywords)
  307. X    hdrline[i++] = MakeLowerCopy(hp->keywords);
  308. X    if (hp->summary)
  309. X    hdrline[i++] = MakeLowerCopy(hp->summary);
  310. X    if (i == 0)
  311. X    return;
  312. X    hdrline[i] = NULL;
  313. X
  314. X    /* For all the newsgroups, see if there's a mapping file. */
  315. X    for (n = Split(hp->nbuf, &groups, NGDELIM), i = 0; i < n; i++) {
  316. X    if (groups[i] == NULL || groups[i][0] == '\0')
  317. X        continue;
  318. X
  319. X    /* Gate the name of the mapping file. */
  320. X#ifdef    IN_ONEPLACE
  321. X    Strcpy(buff, IN_ONEPLACE);
  322. X#endif    /* IN_ONEPLACE */
  323. X#ifdef    IN_SPOOLDIR
  324. X    {
  325. X        register char    *q;
  326. X
  327. X        for (p = buff + APPEND(buff, IN_SPOOLDIR), q = groups[i]; *q; q++)
  328. X        *p++ = *q == '.' ? '/' : *q;
  329. X        Strcpy(p, "/recnews.cmd");
  330. X    }
  331. X#endif    /* IN_SPOOLDIR */
  332. X#ifdef    IN_CMDDIR
  333. X    Sprintf(buff, "%s/%s", IN_CMDDIR, groups[i]);
  334. X#endif    /* IN_CMDDIR */
  335. X
  336. X    if (access(buff, R_OK) >= 0 && (mapline = ReadFile(buff))) {
  337. X        /* For all lines in the file, if there's a command and the
  338. X         * pattern matches, execute the command. */
  339. X        for (j = 0; mapline[j]; j++)
  340. X        if (p = ParsePattern(mapline[j], j))
  341. X            for (t = 0; hdrline[t]; t++)
  342. X            if (re_exec(hdrline[t]) == 1) {
  343. X                DoCommand(hp, p, groups[i]);
  344. X                break;
  345. X            }
  346. X        FreeFile(mapline);
  347. X    }
  348. X    }
  349. X
  350. X    /* Free dynamic space. */
  351. X    for (i = 0; hdrline[i]; i++)
  352. X    free(hdrline[i]);
  353. X    SplitFree(&groups);
  354. X}
  355. X
  356. X
  357. X/*
  358. X**  Signal-catcher and child-reapers.
  359. X*/
  360. X
  361. X
  362. X/*
  363. X**  Exit such that sendmail will again later.
  364. X*/
  365. XSTATIC CATCHER
  366. XSig_tempfail()
  367. X{
  368. X    exit(EX_TEMPFAIL);
  369. X}
  370. X
  371. X
  372. X/*
  373. X**  Reap the inews child properly, and exit with his exit code, so that
  374. X**  ultimate success or failure rests with inews.
  375. X*/
  376. XSTATIC CATCHER
  377. Xchildgone()
  378. X{
  379. X    register int    pid;
  380. X    int            W;
  381. X
  382. X    if ((pid = wait(&W)) != ChildPid || pid == -1)
  383. X    exit(EX_OSERR);
  384. X    
  385. X    /* Was it a good death? */
  386. X    if (WAIT_EXITSIG(W)) {
  387. X    Fprintf(stderr, "%s:  Child %d killed by signal %d.\n",
  388. X        Pname, pid, WAIT_EXITSIG(W));
  389. X    if (WAIT_CORED(W))
  390. X        Fprintf(stderr, "%s:  Child %d dumped core.\n", Pname, pid);
  391. X    exit(EX_SOFTWARE);
  392. X    }
  393. X
  394. X#ifdef    MMDF
  395. X    /* We need a way to tell temporary errors from permanent ones.  Inews
  396. X     * will reject messages because of too much quoting, for example,
  397. X     * so the message will sit in the queue forever.  Until then we'll
  398. X     * have to lose messages on any error. */
  399. X    exit(0);
  400. X#else
  401. X    exit(WAIT_EXITCODE(W));
  402. X#endif    /* MMDF */
  403. X}
  404. X
  405. X
  406. X
  407. X/*
  408. X**  Convert the characters following dots to upper case, if they're
  409. X**  lower case.  Two dots in a row will leave one dot in their place.
  410. X**  Modifies the argument.
  411. X*/
  412. XSTATIC char *
  413. XHackPeriods(string)
  414. X    char        *string;
  415. X{
  416. X    register char    *s;
  417. X    register char    *p;
  418. X
  419. X    if (string) {
  420. X    for (p = s = string; *p; *s++ = *p++)
  421. X        if (*p == '.') {
  422. X        if (*++p == '\0') {
  423. X            *s++ = '.';
  424. X            break;
  425. X        }
  426. X        if (islower(*p))
  427. X            *p = toupper(*p);
  428. X        }
  429. X    *s = '\0';
  430. X    }
  431. X    return string;
  432. X}
  433. X
  434. X
  435. X
  436. Xmain(ac, av)
  437. X    register int    ac;
  438. X    register char    *av[];
  439. X{
  440. X    register char    **vec;
  441. X    register char    *p;
  442. X    register FILE    *F;
  443. X    register FILE    *Infile;
  444. X    HBUF        H;
  445. X    char        **iv;
  446. X    char        buff[BUFSIZ];
  447. X    int            fd[2];
  448. X    int            FlushSubRequests;
  449. X    int            SubjectRequired;
  450. X    int            GotEquals;
  451. X
  452. X    Pname = ((Pname = RDX(av[0], '/')) ? Pname + 1 : av[0]);
  453. X    Infile = stdin;
  454. X
  455. X    /* Remove any trace of who we are. */
  456. X    TrimEnvironment();
  457. X
  458. X    /* So that cores will actually drop... */
  459. X    if (chdir("/tmp") < 0) {
  460. X    Fprintf(stderr, "%s:  Can't chdir(/tmp), %s.\n", Pname,
  461. X        strerror(errno));
  462. X    exit(EX_TEMPFAIL);
  463. X    }
  464. X
  465. X    /* If someone wants to shut down the system, tell sendmail to
  466. X     * try again later. */
  467. X    Signal(SIGTERM, Sig_tempfail);
  468. X
  469. X#ifdef    SENDMAIL
  470. X    /* First read should fetch us the UNIX From_ line.  Not done in MMDF. */
  471. X    if (fgets(buff, sizeof buff, Infile) == NULL)
  472. X    exit(EX_NOINPUT);
  473. X
  474. X    if (!EQn(buff, "From ", 5)) {
  475. X    Fprintf(stderr, "%s:  Input didn't start with UNIX From line:\n",
  476. X        Pname);
  477. X    Fprintf(stderr,"\t%s.\n", buff);
  478. X    exit(EX_DATAERR);
  479. X    }
  480. X#endif    /* SENDMAIL */
  481. X
  482. X    /* Read the mail header. */
  483. X    rfc822read(&H, Infile, buff, sizeof buff);
  484. X
  485. X    /* Process the argument list, copying anything that we don't recognize
  486. X     * over to the inews argument list and changing things as we see fit. */
  487. X    FlushSubRequests = FALSE;
  488. X    GotEquals = FALSE;
  489. X    SubjectRequired = TRUE;
  490. X    iv = NEW(char*, ac+ 2);
  491. X    iv[0] = INEWS;
  492. X    iv[1] = "-h";
  493. X    for (vec = iv + 2; p = *++av; )
  494. X    if (p[0] != '-')
  495. X        *vec++ = p;
  496. X     else
  497. X        switch(p[1]) {
  498. X        case 'x':
  499. X        SubjectRequired = FALSE;
  500. X        /* FALLTHROUGH */
  501. X        default:
  502. X        *vec++ = p;
  503. X        break;
  504. X        case '=':
  505. X        if (!GotEquals)
  506. X            iv++;
  507. X        iv[0] = p[2] ? &p[2] : *++av;
  508. X        GotEquals++;
  509. X        break;
  510. X        case '.':
  511. X        Debugging++;
  512. X        break;
  513. X        case 'n':
  514. X        /* Newsgroup this messages goes to. */
  515. X        Strcpy(H.nbuf, p[2] ? &p[2] : *++av);
  516. X        break;
  517. X        case 'o':
  518. X        /* Default organization. */
  519. X        if (H.organization[0] == '\0')
  520. X            Strcpy(H.organization, HackPeriods(p[2] ? &p[2] : *++av));
  521. X        else if (p[2] == '\0')
  522. X            av++;
  523. X        SubjectRequired = FALSE;
  524. X        break;
  525. X        case 'F':
  526. X        FlushSubRequests = TRUE;
  527. X        break;
  528. X        }
  529. X    *vec++ = NULL;
  530. X
  531. X    /* Bash on the mail header. */
  532. X    if (p = HackHeader(&H, SubjectRequired)) {
  533. X    Fprintf(stderr, "%s:  Rejected by netnews because:\n", Pname);
  534. X    Fprintf(stderr, "\t%s.\n", p);
  535. X    if (H.nbuf[0])
  536. X        Fprintf(stderr, "\tIt was going into the newsgroup%s %s.\n",
  537. X        IDX(H.nbuf, NGDELIM) ? "s" : "", H.nbuf);
  538. X    exit(EX_DATAERR);
  539. X    }
  540. X    Editnewsgroups(&H);
  541. X
  542. X    if (Debugging) {
  543. X    for (vec = iv; *vec; vec++)
  544. X        (void)printf(" |%s| ", *vec);
  545. X    (void)printf("\n");
  546. X    if (!rfc822write(&H, stdout))
  547. X        Fprintf(stderr, "%s:  Can't write header, %s.\n",
  548. X        Pname, strerror(errno));
  549. X    while (fgets(buff, sizeof buff, Infile))
  550. X        Fputs(buff, stdout);
  551. X    exit(EX_OK);
  552. X    }
  553. X
  554. X    if (FlushSubRequests && (Infile = IsSubRequest(Infile)) == NULL) {
  555. X    Fprintf(stderr, "%s:  Rejected by netnews becase:\n", Pname);
  556. X    Fprintf(stderr, "\tIt seems like a subscription request.\n");
  557. X    exit(EX_DATAERR);
  558. X    }
  559. X
  560. X    /* Get ready to spawn an inews. */
  561. X    if (pipe(fd) < 0) {
  562. X    Fprintf(stderr, "%s:  Can't pipe, %s.\n", Pname, strerror(errno));
  563. X    exit(EX_TEMPFAIL);
  564. X    }
  565. X    Fflush(stderr);
  566. X    Fflush(stdout);
  567. X#ifdef    SIGCHLD
  568. X    Signal(SIGCHLD, childgone);
  569. X#endif    /* SIGCHLD */
  570. X#ifdef    SIGCLD
  571. X    Signal(SIGCLD, childgone);
  572. X#endif    /* SIGCLD */
  573. X
  574. X    if ((ChildPid = fork()) < 0) {
  575. X    Fprintf(stderr,"%s:  Can't fork, %s.\n", Pname, strerror(errno));
  576. X    exit(EX_TEMPFAIL);
  577. X    }
  578. X    if (ChildPid == 0) {
  579. X    /* Redirect I/O; it's unlikely the test below will fail. */
  580. X    if (fd[PIPE_READER] != STDIN) {
  581. X        Close(STDIN);
  582. X        if (dup(fd[PIPE_READER]) != STDIN)
  583. X        Fprintf(stderr, "%s:  Can't redirect input, %s.\n",
  584. X            Pname, strerror(errno));
  585. X    }
  586. X    Close(fd[PIPE_READER]);
  587. X    Close(fd[PIPE_WRITER]);
  588. X    (void)execv(iv[0], iv);
  589. X    Fprintf(stderr, "%s:  Can't exec %s, %s.\n",
  590. X        Pname, iv[0], strerror(errno));
  591. X    exit(EX_OSERR);
  592. X    }
  593. X
  594. X    /* Set things up after the fork. */
  595. X    Close(fd[PIPE_READER]);
  596. X    Signal(SIGPIPE, childgone);
  597. X    if ((F = fdopen(fd[PIPE_WRITER], "w")) == NULL)
  598. X    exit(EX_OSERR);
  599. X
  600. X    /* Stuff the header. */
  601. X    if (!rfc822write(&H, F)) {
  602. X    Fprintf(stderr, "%s:  Can't write header, %s.\n",
  603. X        Pname, strerror(errno));
  604. X    exit(EX_IOERR);
  605. X    }
  606. X
  607. X    /* Write the rest of the message. */
  608. X    while (fgets(buff, sizeof buff, Infile)) {
  609. X    Fputs(buff, F);
  610. X    if (ferror(F))
  611. X        break;
  612. X    }
  613. X
  614. X    /* Close down the pipe. */
  615. X    Fflush(F);
  616. X    if (ferror(F)) {
  617. X    Fprintf(stderr, "%s:  Error flushing pipe to news, %s.\n",
  618. X        Pname, strerror(errno));
  619. X    exit(EX_IOERR);
  620. X    }
  621. X    if (fclose(F) == EOF)
  622. X    Fprintf(stderr, "%s:  Error closing pipe to news, %s.\n",
  623. X        Pname, strerror(errno));
  624. X
  625. X    /* Wait for inews, and exit as it does. */
  626. X    childgone();
  627. X    /* NOTREACHED */
  628. X}
  629. END_OF_FILE
  630.   if test 14113 -ne `wc -c <'mail2news.c'`; then
  631.     echo shar: \"'mail2news.c'\" unpacked with wrong size!
  632.   fi
  633.   # end of 'mail2news.c'
  634. fi
  635. if test -f 'mkmailpost.sh' -a "${1}" != "-c" ; then 
  636.   echo shar: Will not clobber existing file \"'mkmailpost.sh'\"
  637. else
  638.   echo shar: Extracting \"'mkmailpost.sh'\" \(1206 characters\)
  639.   sed "s/^X//" >'mkmailpost.sh' <<'END_OF_FILE'
  640. X#! /bin/sh
  641. X##  $Header: /nfs/papaya/u2/rsalz/src/newsgate/src/RCS/mkmailpost.sh,v 1.4 91/02/12 14:49:26 rsalz Exp $
  642. X##  Script to make a set of "mailpost" commands for all groups in
  643. X##  the news active file.
  644. X##  Usage:
  645. X##    makemailpost [news active file [mail2newspath] ]
  646. X
  647. Xcase $# in
  648. X[012])
  649. X    ;;
  650. X*)
  651. X    echo "Usage: `basename $0` [activefile [mail2newspath] ]" 1>&2
  652. X    exit 1
  653. X    ;;
  654. Xesac
  655. X
  656. XACTIVE=${1-/usr/lib/news/active}
  657. Xif [ ! -f ${ACTIVE} ] ; then
  658. X    echo "`basename $0`:  Can't read input ${ACTIVE}." 1>&2
  659. X    exit 1
  660. Xfi
  661. X
  662. X##  Write the prolog.
  663. Xecho "/*"
  664. Xecho "**  Generated from ${ACTIVE}"
  665. Xecho "**  by `whoami` on `date`"
  666. Xecho "*/"
  667. Xif [ "$2" != "" ] ; then
  668. X    echo 'default mail2news "'$2'";'
  669. Xfi
  670. X
  671. Xtrap "rm -f /tmp/mmp$$ ; exit 1" 1 2 3 15
  672. X
  673. X##  Create the sed temporary file and run it
  674. Xcat >/tmp/mmp$$ <<\EOF
  675. X# Delete moderated groups
  676. X/m$/d
  677. X# Trim anything after the first field
  678. Xs/ .*$//
  679. X# Delete control, junk, site-specific
  680. X/^control$/d
  681. X/^junk$/d
  682. X/^to\./d
  683. X# Delete test groups and announce, assuming the latter is moderated
  684. X/\<test\>/d
  685. X/\<announce\>/d
  686. X# Turn the line "foo" into "mailpost foo;"
  687. Xs/^.*$/mailpost &;/
  688. XEOF
  689. X
  690. X##  Run sed, clean up.
  691. Xsed -f /tmp/mmp$$ <${ACTIVE} | sort
  692. X
  693. Xrm -f /tmp/mmp$$
  694. END_OF_FILE
  695.   if test 1206 -ne `wc -c <'mkmailpost.sh'`; then
  696.     echo shar: \"'mkmailpost.sh'\" unpacked with wrong size!
  697.   fi
  698.   # end of 'mkmailpost.sh'
  699. fi
  700. if test -f 'news2mail.c' -a "${1}" != "-c" ; then 
  701.   echo shar: Will not clobber existing file \"'news2mail.c'\"
  702. else
  703.   echo shar: Extracting \"'news2mail.c'\" \(12785 characters\)
  704.   sed "s/^X//" >'news2mail.c' <<'END_OF_FILE'
  705. X/*
  706. X**  NEWS2MAIL
  707. X**  Read a news article on standard input, and send it to the mailing
  708. X**  list as directed by the command-line arguments.  It does some
  709. X**  parsing and converting of news headers into mail headers.
  710. X**
  711. X**  This program wants to lie to sendmail, so it should be setuid to
  712. X**  one of the "trusted" users as listed in your sendmail.cf file.
  713. X**
  714. X*/
  715. X#include "gate.h"
  716. X#include <sys/stat.h>
  717. X#ifdef    RCSID
  718. Xstatic char RCS[] =
  719. X    "$Header: /nfs/papaya/u2/rsalz/src/newsgate/src/RCS/news2mail.c,v 1.10 91/02/12 14:50:11 rsalz Exp $";
  720. X#endif    /* RCSID */
  721. X
  722. X
  723. X/* Flags for special header lines. */
  724. Xtypedef enum _HEADERTYPE {
  725. X    HDR_NORM,
  726. X    HDR_SUBJ,
  727. X    HDR_CTRL,
  728. X    HDR_REFS,
  729. X    HDR_PATH,
  730. X    HDR_FROM
  731. X} HEADERTYPE;
  732. X
  733. X
  734. X/* Header-cracking datatype. */
  735. Xtypedef struct _HEADER {
  736. X    char        *Tag;
  737. X    int            Length;
  738. X    HEADERTYPE        Flag;
  739. X    char        Value[SM_SIZE];
  740. X} HEADER;
  741. X
  742. XSTATIC int    Debugging;
  743. Xchar        *Pname;
  744. X
  745. X/* The headers we care about. */
  746. XSTATIC HEADER    Table[] = {
  747. X    {    "Control",     7,     HDR_CTRL    },
  748. X    {    "Date",         4,     HDR_NORM    },
  749. X    {    "From",         4,     HDR_FROM    },
  750. X    {    "Message-ID",    10,     HDR_NORM    },
  751. X    {    "Organization",    12,     HDR_NORM    },
  752. X    {    "Path",         4,     HDR_PATH    },
  753. X    {    "References",    10,     HDR_REFS    },
  754. X    {    "Reply-To",     8,     HDR_NORM    },
  755. X    {    "Subject",     7,     HDR_SUBJ    },
  756. X};
  757. X
  758. X
  759. X/*
  760. X**  Figure out the return address, by doing some optimization on the
  761. X**  path.  If we find an Internet host or a UUCP neighbor, remove all
  762. X**  hosts before that one from the path.
  763. X*/
  764. XSTATIC char *
  765. XEditPath(path, Host)
  766. X    register char    *path;
  767. X    char        *Host;
  768. X{
  769. X    static char        **Hinet;
  770. X    static char        **Huucp;
  771. X    static char        **Lsys;
  772. X    static char        buff[SM_SIZE];
  773. X    register char    *p;
  774. X    register char    *q;
  775. X    register char    **V;
  776. X    register int    ac;
  777. X    register int    i;
  778. X    register int    uucppal;
  779. X    register int    inetpal;
  780. X    FILE        *F;
  781. X    FILE        *P;
  782. X    struct stat        Sb1;
  783. X    struct stat        Sb2;
  784. X    char        **av;
  785. X    char        *Inetsite;
  786. X
  787. X    if ((ac = Split(path, &av, '!')) == 2) {
  788. X    /* Simple case:  "site!user" */
  789. X    Strcpy(buff, av[1]);
  790. X    SplitFree(&av);
  791. X    return buff;
  792. X    }
  793. X
  794. X    /* Initialize.  This is silly for now, but eventually we might want
  795. X     * to be able to handle a batched feed. */
  796. X#ifdef    UUNAME
  797. X    if (Lsys == NULL) {
  798. X    p = UUNAME;
  799. X    /* If someone diddled L.sys, rebuild uuname output. */
  800. X    if (stat(L_SYS, &Sb1) < 0 || stat(p, &Sb2) < 0
  801. X      || Sb1.st_mtime >= Sb2.st_mtime)
  802. X        if ((F = fopen(p, "w"))  == NULL)
  803. X        Fprintf(stderr, "%s:  Can't create %s, %s.\n",
  804. X            Pname, p, strerror(errno));
  805. X        else {
  806. X        if ((P = popen("exec uuname", "r")) == NULL)
  807. X            Fprintf(stderr, "%s:  popen failed, %s.\n",
  808. X                Pname, strerror(errno));
  809. X        else {
  810. X            while (fgets(buff, sizeof buff, P))
  811. X            Fputs(buff, F);
  812. X            if (pclose(P))
  813. X            Fprintf(stderr, "%s:  pclose failed, %s.\n",
  814. X                Pname, strerror(errno));
  815. X        }
  816. X        if (fclose(F) == EOF)
  817. X            Fprintf(stderr, "%s:  Error closing %s, %s.\n",
  818. X                Pname, p, strerror(errno));
  819. X        }
  820. X
  821. X    /* Slurp up names of UUCP hosts we talk to. */
  822. X    Lsys = ReadFile(p);
  823. X
  824. X#ifdef    UUCP_INET
  825. X    /* Slurp up the UUCP->Internet name mappings. */
  826. X    for (Huucp = ReadFile(UUCP_INET), i = 1; Huucp[i]; i++)
  827. X        ;
  828. X    for (Hinet = NEW(char*, i), i = 0; p = Huucp[i]; i++) {
  829. X        while (*p && !WHITE(*p))
  830. X        p++;
  831. X        if (*p)
  832. X        for (*p++ = '\0'; *p && WHITE(*p); p++)
  833. X            ;
  834. X        Hinet[i] = p;
  835. X    }
  836. X#endif    /* UUCP_INET */
  837. X    }
  838. X#endif    /* UUNAME */
  839. X
  840. X    /* Scan the path, noting if we find a UUCP or Internet neighbor. */
  841. X    for (uucppal = 1, inetpal = 0, i = 0; i < ac; i++) {
  842. X    for (V = Lsys; *V; V++)
  843. X        if (EQ(av[i], *V))
  844. X        uucppal = i;
  845. X    for (V = Huucp; *V; V++)
  846. X        if (EQ(av[i], *V)) {
  847. X        inetpal = i;
  848. X        Inetsite = Hinet[V - Huucp];
  849. X        }
  850. X    }
  851. X
  852. X    if (inetpal < uucppal) {
  853. X    /* No Internet site found, turn a!b!c into a!b!c@this-host */
  854. X    for (p = buff + APPEND(buff, av[uucppal]); ++uucppal < ac; ) {
  855. X        *p++ = '!';
  856. X        p += APPEND(p, av[uucppal]);
  857. X    }
  858. X    *p++ = '@';
  859. X    Strcpy(p, Host);
  860. X    }
  861. X    else if (inetpal == ac - 1)
  862. X    /* Turn a!b!inet!user into user@inet.domain.name */
  863. X    Sprintf(buff, "%s@%s", av[ac], Inetsite);
  864. X    else {
  865. X    /* Turn a!inet!b!user into b!user@inet.domain.name */
  866. X    for (p = buff + APPEND(buff, av[++inetpal]); ++inetpal < ac; ) {
  867. X        *p++ = '!';
  868. X        p += APPEND(p, av[inetpal]);
  869. X    }
  870. X    *p++ = '@';
  871. X    Strcpy(p, Inetsite);
  872. X    }
  873. X
  874. X    /* Convert all but the last "@" to "%" (fie on decwrl and psuecl!). */
  875. X    if (p = IDX(buff, '@'))
  876. X    for ( ; q = IDX(p + 1, '@'); p = q)
  877. X        *p = '%';
  878. X
  879. X    SplitFree(&av);
  880. X    return buff;
  881. X}
  882. X
  883. X
  884. X/*
  885. X**  Hack up the references, taking only the last three.
  886. X*/
  887. XSTATIC char *
  888. XTrimReferences(refs)
  889. X    char         *refs;
  890. X{
  891. X    static char        buff[SM_SIZE];
  892. X    register char    *p;
  893. X    register int    i;
  894. X    register int    ac;
  895. X    char        **av;
  896. X
  897. X    if (ac = Split(refs, &av, '\0')) {
  898. X    /* Tricky.  If there are five references, we want subscripts 2,3,4. */
  899. X    i = ac < 3 ? 0 : ac - 3;
  900. X    for (p = buff + APPEND(buff, av[i]); ++i < ac; ) {
  901. X        *p++ = ',';
  902. X        *p++ = ' ';
  903. X        p += APPEND(p, av[i]);
  904. X    }
  905. X    SplitFree(&av);
  906. X    }
  907. X    else
  908. X    buff[0] = '\0';
  909. X    return buff;
  910. X}
  911. X
  912. X
  913. X#ifndef    HAVE_PUTENV
  914. X/*
  915. X**  A brute-forced implementation of putenv.  Wastes memory.  Consider
  916. X**  it incentive to install the free BSD version...
  917. X*/
  918. Xint
  919. Xputenv(val)
  920. X    char    *val;
  921. X{
  922. X    char    **new;
  923. X    int        i;
  924. X    int        length;
  925. X    int        found;
  926. X    char    *p;
  927. X
  928. X    /* See if the value is already in the environment. */
  929. X    found = -1;
  930. X    if (p = IDX(val, '=')) {
  931. X    for (length = ++p - val, i = 0; environ[i]; i++)
  932. X        if (EQn(val, environ[i], length)) {
  933. X        found = i;
  934. X        break;
  935. X        }
  936. X    }
  937. X
  938. X    /* Get the size, and space for the new environment. */
  939. X    for (i = 0; environ[i]; i++)
  940. X    ;
  941. X    i += 2;
  942. X    new = NEW(char*, i);
  943. X    new[0] = val;
  944. X
  945. X    /* Copy the old to the new. */
  946. X    for (i = 0; environ[i]; i++)
  947. X    if (i != found)
  948. X        new[i + 1] = environ[i];
  949. X    new[i + 1] = NULL;
  950. X    environ = new;
  951. X    return 0;
  952. X}
  953. X#endif    /* HAVE_PUTENV */
  954. X
  955. X
  956. X/*
  957. X**  Print a usage message and exit.
  958. X*/
  959. XSTATIC void
  960. XUsage()
  961. X{
  962. X    Fprintf(stderr, "Usage:\n\t%s %s %s\n",
  963. X    Pname,
  964. X    "[-.] [-e var=val]",
  965. X    "listname listaddr listadmin host [article]");
  966. X    exit(EX_USAGE);
  967. X}
  968. X
  969. X
  970. Xmain(ac, av)
  971. X    int            ac;
  972. X    register char    *av[];
  973. X{
  974. X    static char        tmp[sizeof TEMPFILE];
  975. X    register FILE    *F;
  976. X    register HEADER    *hp;
  977. X    register char    *p;
  978. X    char        *sv[10];
  979. X    char        buff[BUFSIZ];
  980. X    char        SenderAddr[SM_SIZE];
  981. X    char        ToAddr[SM_SIZE];
  982. X    char        Host[SM_SIZE];
  983. X    char        Fullname[SM_SIZE];
  984. X    int            i;
  985. X    int            HadEflag;
  986. X    char        *Listname;
  987. X    char        *Listaddr;
  988. X    char        *Listadmin;
  989. X    char        *Listhost;
  990. X    char        *Article;
  991. X
  992. X    /* Set defaults. */
  993. X    Pname = (Pname = RDX(av[0], '/')) ? Pname + 1 : av[0];
  994. X    HadEflag = FALSE;
  995. X
  996. X    /* Parse JCL. */
  997. X    while ((i = getopt(ac, av, "E:.")) != EOF)
  998. X    switch (i) {
  999. X    default:
  1000. X        Usage();
  1001. X        /* NOTREACHED */
  1002. X    case '.':
  1003. X        Debugging = TRUE;
  1004. X        break;
  1005. X    case 'E':
  1006. X        if (putenv(COPY(optarg))) {
  1007. X        Fprintf(stderr, "%s:  Can't add to environment, %s.\n",
  1008. X            Pname, strerror(errno));
  1009. X        exit(EX_TEMPFAIL);
  1010. X        }
  1011. X        HadEflag = TRUE;
  1012. X        break;
  1013. X    }
  1014. X    ac -= optind;
  1015. X    av += optind;
  1016. X    if (ac != 4 && ac != 5)
  1017. X    Usage();
  1018. X
  1019. X    /* Parse the positional parameters. */
  1020. X    Listname = av[0];
  1021. X    Listaddr = av[1];
  1022. X    Listadmin = av[2];
  1023. X    Listhost = av[3];
  1024. X    Article = av[4];
  1025. X
  1026. X    /* Arrange for logging. */
  1027. X    if (!Debugging && freopen(ERR_LOG, "a", stderr) == NULL)
  1028. X    /* Sigh; error in error handler.... */
  1029. X    (void)freopen("/dev/console", "w", stderr);
  1030. X
  1031. X    if (Article && freopen(Article, "r", stdin) == NULL) {
  1032. X    Fprintf(stderr, "%s:  Can't open %s, %s.\n",
  1033. X        Pname, Article, strerror(errno));
  1034. X    exit(EX_NOINPUT);
  1035. X    }
  1036. X
  1037. X    /* Who are we? */
  1038. X    if (gethostname(Host, sizeof Host) < 0) {
  1039. X    Fprintf(stderr, "%s:  Can't get hostname, %s.\n",
  1040. X        Pname, strerror(errno));
  1041. X    exit(EX_TEMPFAIL);
  1042. X    }
  1043. X
  1044. X    /* Read headers, storing the ones we want. */
  1045. X    while (fgets(buff, sizeof buff, stdin)) {
  1046. X    if (p = IDX(buff, '\n'))
  1047. X        *p = '\0';
  1048. X    else
  1049. X        Fprintf(stderr, "%s:  Header line too long (%d bytes max)\n\t%s\n",
  1050. X        Pname, sizeof buff, buff);
  1051. X    if (p == buff)
  1052. X        /* Blank line means end of headers. */
  1053. X        break;
  1054. X
  1055. X    for (hp = Table; hp < ENDOF(Table); hp++)
  1056. X        if (buff[hp->Length] == ':' && EQn(hp->Tag, buff, hp->Length)) {
  1057. X        /* Skip whitespace. */
  1058. X        for (p = &buff[hp->Length]; *p && WHITE(*p); p++)
  1059. X            ;
  1060. X        switch (hp->Flag) {
  1061. X        case HDR_SUBJ:
  1062. X            if (!EQn(p, "cmsg ", 5)) {
  1063. X            Strcpy(hp->Value, p);
  1064. X            break;
  1065. X            }
  1066. X            /* FALLTHROUGH */
  1067. X        case HDR_CTRL:
  1068. X            /* Eat rest of message. */
  1069. X            while (fgets(buff, sizeof buff, stdin))
  1070. X            ;
  1071. X            exit(EX_OK);
  1072. X            /* NOTREACHED */
  1073. X        case HDR_NORM:
  1074. X            Strcpy(hp->Value, p);
  1075. X            break;
  1076. X        case HDR_FROM:
  1077. X            /* Turn "joe@site.uucp (My Name)" into "(My Name)" */
  1078. X            if ((p = IDX(p, ' ')) && p[1] == '(')
  1079. X            Strcpy(Fullname, ++p);
  1080. X            else
  1081. X            Fullname[0] = '\0';
  1082. X            break;
  1083. X        case HDR_REFS:
  1084. X            Strcpy(hp->Value, TrimReferences(p));
  1085. X            break;
  1086. X        case HDR_PATH:
  1087. X            Strcpy(hp->Value, EditPath(p, Host));
  1088. X            break;
  1089. X        }
  1090. X    }
  1091. X    }
  1092. X
  1093. X    /* Set up temp output. */
  1094. X    if ((F = fopen(mktemp(strcpy(tmp, TEMPFILE)), "w")) == NULL) {
  1095. X    Fprintf(stderr, "%s:  Can't create %s, %s.\n", tmp, strerror(errno));
  1096. X    exit(EX_CANTCREAT);
  1097. X    }
  1098. X
  1099. X    if (IDX(Listadmin, '@'))
  1100. X    Strcpy(SenderAddr, Listadmin);
  1101. X    else
  1102. X    Sprintf(SenderAddr, "%s@%s", Listadmin, Listhost);
  1103. X    if (IDX(Listaddr, '@'))
  1104. X    Strcpy(ToAddr, Listaddr);
  1105. X    else
  1106. X    Sprintf(ToAddr, "%s@%s", Listaddr, Listhost);
  1107. X
  1108. X    /* Print out a sanitized header for mail. */
  1109. X    if (IDX(Listname, '@'))
  1110. X    Strcpy(buff, Listname);
  1111. X    else
  1112. X    Sprintf(buff, "%s@%s", Listname, Listhost);
  1113. X    Fprintf(F, "Received: from USENET by %s with netnews\n", Host);
  1114. X    Fprintf(F, "\tfor %s (%s);\n", ToAddr, buff);
  1115. X    Fprintf(F, "\tcontact usenet@%s if you have questions.\n", Host);
  1116. X    Fprintf(F, "To: %s\n", buff);
  1117. X    for (hp = Table; hp < ENDOF(Table); hp++)
  1118. X    if (hp->Flag == HDR_PATH) {
  1119. X        Fprintf(F, "From: %s %s\n", hp->Value, Fullname);
  1120. X        Fprintf(F, "Sender: %s\n", SenderAddr);
  1121. X    }
  1122. X    else if (hp->Value[0])
  1123. X        Fprintf(F, "%s %s\n", hp->Tag, hp->Value);
  1124. X    Fprintf(F, "\n");
  1125. X
  1126. X    /* Dump the body of the message. */
  1127. X    while (fgets(buff, sizeof buff, stdin))
  1128. X    Fputs(buff, F);
  1129. X    if (fclose(F) == EOF)
  1130. X    Fprintf(stderr, "%s:  Error closing %s, %s.\n",
  1131. X        Pname, tmp, strerror(errno));
  1132. X
  1133. X    /* Set I/O correctly.  Stderr is going to the log, stdin should be the
  1134. X     * message we created.  Easiest thing is to unlink an open file. */
  1135. X    if (freopen(tmp, "r", stdin) == NULL) {
  1136. X    Fprintf(stderr, "%s:  Can't open %s for reading, %s.\n",
  1137. X        Pname, tmp, strerror(errno));
  1138. X    exit(EX_OSERR);
  1139. X    }
  1140. X    if (unlink(tmp) < 0)
  1141. X    Fprintf(stderr, "%s:  Can't unlink %s, %s.\n",
  1142. X        Pname, tmp, strerror(errno));
  1143. X
  1144. X    /* Common code for all argument vectors. */
  1145. X    i = 0;
  1146. X
  1147. X#ifdef    MAILSCRIPT
  1148. X    /* Build the MAILSCRIPT argument vector. */
  1149. X    sv[i++] = MAILSCRIPT;
  1150. X    /* Headers are inline. */
  1151. X    sv[i++] = "-ASIS";
  1152. X    /* Set the logical sender/from address. */
  1153. X    sv[i++] = "-From";
  1154. X    sv[i++] = SenderAddr;
  1155. X    /* Set the recipient. */
  1156. X    sv[i++] = "-recip";
  1157. X    sv[i++] = ToAddr;
  1158. X#endif    /* MAILSCRIPT */
  1159. X
  1160. X#ifdef    SENDMAIL
  1161. X    /* Build of the SENDMAIL argument vector. */
  1162. X    sv[i++] = SENDMAIL;
  1163. X    /* Ignore periods as message terminator (same as -oi). */
  1164. X    sv[i++] = "-i";
  1165. X    /* Queued delivery. */
  1166. X    sv[i++] = "-odq";
  1167. X    /* Set the "From:" address. */
  1168. X    sv[i++] = "-f";
  1169. X    sv[i++] = SenderAddr;
  1170. X    /* Set the recipient. */
  1171. X    sv[i++] = ToAddr;
  1172. X#endif    /* SENDMAIL */
  1173. X
  1174. X#ifdef    MMDF
  1175. X    /* Build of the MMDF argument vector. */
  1176. X    sv[i++] = MMDF;
  1177. X    /* Deliver to mailbox (m), deliver all mail now (ln), trust me (t), send
  1178. X     * no warnings (z), return to "Sender:" (s), get recipients from the
  1179. X     * "To:" address (xto*). */
  1180. X    sv[i++] = "-mlntzsxto*";
  1181. X    /* Set the From: address. */
  1182. X    sv[i++] = SenderAddr;
  1183. X#endif    /* MMDF */
  1184. X
  1185. X    /* Null-terminate the vector. */
  1186. X    sv[i] = NULL;
  1187. X
  1188. X    if (Debugging) {
  1189. X    for (i = 0; sv[i]; i++)
  1190. X        (void)printf(" |%s| ", sv[i]);
  1191. X    (void)printf("\n");
  1192. X    if (HadEflag) {
  1193. X        for (i = 0; sv[i]; i++)
  1194. X        (void)printf(" [%s] ", sv[i]);
  1195. X        (void)printf("\n");
  1196. X    }
  1197. X    while (fgets(buff, sizeof buff, stdin))
  1198. X        Fputs(buff, stdout);
  1199. X    exit(EX_OK);
  1200. X    }
  1201. X
  1202. X    /* Try to setuid, if desired.  Could "factor out" the execv from the
  1203. X     * #ifdef, but I hate the way the resultant "dangling else" looks. */
  1204. X#ifdef    TRUSTED
  1205. X    if (setuid(TRUSTED) < 0)
  1206. X    Fprintf(stderr, "%s:  Can't setuid to %d, %s.\n",
  1207. X        Pname, TRUSTED, strerror(errno));
  1208. X    else
  1209. X    (void)execv(sv[0], sv);
  1210. X#else
  1211. X    (void)execv(sv[0], sv);
  1212. X#endif    /* TRUSTED */
  1213. X
  1214. X    /* Something failed; dump the message and quit */
  1215. X    Fprintf(stderr, "%s:  Can't execv %s, %s.\n",
  1216. X    Pname, sv[0], strerror(errno));
  1217. X    while (fgets(buff, sizeof buff, stdin))
  1218. X    Fputs(buff, stdout);
  1219. X    exit(EX_OSERR);
  1220. X    /* NOTREACHED */
  1221. X}
  1222. END_OF_FILE
  1223.   if test 12785 -ne `wc -c <'news2mail.c'`; then
  1224.     echo shar: \"'news2mail.c'\" unpacked with wrong size!
  1225.   fi
  1226.   # end of 'news2mail.c'
  1227. fi
  1228. if test -f 'regex.3' -a "${1}" != "-c" ; then 
  1229.   echo shar: Will not clobber existing file \"'regex.3'\"
  1230. else
  1231.   echo shar: Extracting \"'regex.3'\" \(7421 characters\)
  1232.   sed "s/^X//" >'regex.3' <<'END_OF_FILE'
  1233. X.\" $Header: /nfs/papaya/u2/rsalz/src/newsgate/src/RCS/regex.3,v 1.4 91/02/12 14:50:56 rsalz Exp $
  1234. X.TH REGEX 3 LOCAL
  1235. X.SH NAME
  1236. Xre_comp, re_exec, re_subs, re_modw, re_fail \- regular expression handling
  1237. X.SH SYNOPSIS
  1238. X.nf
  1239. X.B "char *re_comp(pat)"
  1240. X.B "char *pat;"
  1241. X.sp
  1242. X.B "re_exec(str)"
  1243. X.B "char *str;"
  1244. X.sp
  1245. X.B "re_subs(src, dst)"
  1246. X.B "char *src;"
  1247. X.B "char *dst;"
  1248. X.sp
  1249. X.B "void re_fail(msg, op)"
  1250. X.B "char *msg;"
  1251. X.B "char op;"
  1252. X.sp
  1253. X.B "void re_modw(str)"
  1254. X.B "char *str;"
  1255. X.fi
  1256. X.SH DESCRIPTION
  1257. XThese functions implement
  1258. X.IR ed (1)\-style
  1259. Xpartial regular expressions and supporting facilities.
  1260. X.PP
  1261. X.I Re_comp
  1262. Xcompiles a pattern string into an internal form (a deterministic finite\-state
  1263. Xautomaton) to be executed by
  1264. X.I re_exec
  1265. Xfor pattern matching.
  1266. X.I Re_comp
  1267. Xreturns zero if the pattern is compiled successfully, otherwise it returns an
  1268. Xerror message string.
  1269. XIf
  1270. X.I re_comp
  1271. Xis called with a null pointer or a pointer to an empty string, it returns
  1272. Xwithout changing the currently compiled regular expression.
  1273. X.PP
  1274. X.I Re_comp
  1275. Xsupports the same limited set of
  1276. X.I "regular expressions"
  1277. Xfound in
  1278. X.I ed
  1279. Xand Berkeley
  1280. X.IR regex (3)
  1281. Xroutines:
  1282. X.in +1i
  1283. X.de Ti
  1284. X.sp
  1285. X.ti -1i
  1286. X.ta 0.5i +0.5i +0.5i
  1287. X..
  1288. X.Ti
  1289. X[1]    \fIchar\fP    Matches itself, unless it is a special
  1290. Xcharacter (meta\-character): \fB. \e [ ] * + ^ $\fP
  1291. X.Ti
  1292. X[2]    \fB.\fP    Matches \fIany\fP character.
  1293. X.Ti
  1294. X[3]    \fB\e\fP    Matches the character following it, except
  1295. Xwhen followed by a digit, \fB(\fP, \fB)\fP, \fB<\fP or \fB>\fP
  1296. X(see [7], [8] and [9]).
  1297. XIt is used as an escape character for all other meta\-characters, and itself.
  1298. XWhen used in a set ([4]), it is treated as an ordinary character.
  1299. X.Ti
  1300. X[4]    \fB[\fP\fIset\fP\fB]\fP    Matches one of the characters in the set.
  1301. XIf the first character in the set is \fB^\fP, it matches a character \fInot\fP
  1302. Xin the set.
  1303. Xthe shorthand
  1304. X.IR S \- E
  1305. Xspecifies the set of characters
  1306. X.I S
  1307. Xthrough
  1308. X.IR E ,
  1309. Xinclusive.
  1310. XThe special characters \fB]\fP and \fB\-\fP have no special meaning if they
  1311. Xappear as the first characters in the set.
  1312. X.nf
  1313. X.ta \w'[a\-zA\-Z0\-9]    'u
  1314. XExamples    Match
  1315. X[a\-z]        any lowercase alpha
  1316. X[^]\-]        any char except ] and \-
  1317. X[^A\-Z]        any char except uppercase alpha
  1318. X[a\-zA\-Z0\-9]    any alphanumeric
  1319. X.fi
  1320. X.Ti
  1321. X[5]    \fB*\fP    Any regular expression form [1] to [4], followed by the
  1322. Xclosure character (*) matches zero or more matches of that form.
  1323. X.Ti
  1324. X[6]    \fB+\fP    Same as [5], except it matches one or more.
  1325. X.Ti
  1326. X[7]    \e\|( \e)    A regular expression in the form [1] to [10], enclosed
  1327. Xas \e\|(\fIform\fP\e) matches what form matches.
  1328. XThe enclosure creates a set of tags, used for [8] and for pattern
  1329. Xsubstitution in
  1330. X.IR re_subs .
  1331. XThe tagged forms are numbered starting from one.
  1332. X.Ti
  1333. X[8]    \ed    A \e followed by a digit matches whatever a previously tagged
  1334. Xregular expression ([7]) matched.
  1335. X.Ti
  1336. X[9]    \fB\e<\fP    Matches the beginning of a \fIword\fP; that is,
  1337. Xan empty string followed by a letter, digit, or _ and not preceded by a
  1338. Xletter, digit, or _ .
  1339. X.Ti
  1340. X    \fB\e>\fP    Matches the end of a \fIword\fP; that is, an empty
  1341. Xstring preceded by a letter, digit, or _ , and not followed by a letter,
  1342. Xdigit, or _ .
  1343. X.Ti
  1344. X[10]        A composite regular expression \fIxy\fP where \fIx\fP and
  1345. X\fIy\fP are in the form of [1] to [10] matches the longest match of \fIx\fP
  1346. Xfollowed by a match for \fIy\fP.
  1347. X.Ti
  1348. X[11]    \fB^ $\fP    A regular expression starting with a \fB^\fP character
  1349. Xand/or ending with a \fB$\fP character, restricts the pattern matching to the
  1350. Xbeginning of the line, and/or the end of line (anchors).
  1351. XElsewhere in the pattern, \fB^\fP and \fB$\fP are treated as ordinary
  1352. Xcharacters.
  1353. X.in -1i
  1354. X.PP
  1355. X.I Re_exec
  1356. Xexecutes the internal form produced by
  1357. X.I re_comp
  1358. Xand searches the argument string for the regular expression described
  1359. Xby the internal form.
  1360. X.I Re_exec
  1361. Xreturns 1 if the last regular expression pattern is matched within the string,
  1362. X0 if no match is found.
  1363. XIn case of an internal error (corrupted internal form),
  1364. X.I re_exec
  1365. Xcalls the user\-supplied
  1366. X.I re_fail
  1367. Xand returns 0.
  1368. X.PP
  1369. XThe strings passed to both
  1370. X.I re_comp
  1371. Xand
  1372. X.I re_exec
  1373. Xmay have trailing or embedded newline characters, but must be properly
  1374. Xterminated with a NUL.
  1375. X.PP
  1376. X.I Re_subs
  1377. Xdoes
  1378. X.IR ed \-style
  1379. Xpattern substitution, after a successful match is found by
  1380. X.I re_exec.
  1381. XThe source string parameter to
  1382. X.I re_subs
  1383. Xis copied to the destination string with the following interpretation:
  1384. X.sp
  1385. X.in +1i
  1386. X.Ti
  1387. X[1]    &    Substitute the entire matched string in the destination.
  1388. X.Ti
  1389. X[2]    \e\fId\fP    Substitute the substring matched by a tagged subpattern
  1390. Xnumbered \fId\fP, where \fId\fP is between 1 and 9, inclusive.
  1391. X.Ti
  1392. X[3]    \e\fIc\fP    Treat the next character literally, unless the
  1393. Xcharacter is a digit ([2]).
  1394. X.in -1i
  1395. X.PP
  1396. XIf the copy operation with the substitutions is successful,
  1397. X.I re_subs
  1398. Xreturns 1.
  1399. XIf the source string is corrupted, or the last call to
  1400. X.I re_exec
  1401. Xfails, it returns 0.
  1402. X.PP
  1403. X.I Re_modw
  1404. Xis used to
  1405. Xadd new characters into an internal table to
  1406. Xchange the re_exec's understanding of what
  1407. Xa \fIword\fP should look like, when matching with \fB\e<\fP and \fB\e>\fP
  1408. Xconstructs. If the string parameter is 0 or null string,
  1409. Xthe table is reset back to the default, which contains \fBA\-Z a\-z 0\-9 _\fP .
  1410. X.PP
  1411. X.I Re_fail
  1412. Xis a user\-supplied routine to handle internal errors.
  1413. X.I Re_exec
  1414. Xcalls
  1415. X.I re_fail
  1416. Xwith an error message string, and the opcode character that caused the error.
  1417. XThe default
  1418. X.I re_fail
  1419. Xroutine simply prints the message and the opcode character to the standard
  1420. Xerror and calls
  1421. X.IR exit (2).
  1422. X.SH EXAMPLES
  1423. XFor additional details, refer to the sources.
  1424. X.PP
  1425. X.RS
  1426. X.nf
  1427. X.ta \w'\e\|(foo\e)[1\-3]\e1    'u
  1428. Xfoo*.*    fo foo fooo foobar fobar foxx ...
  1429. X
  1430. Xfo[ob]a[rz]    fobar fooar fobaz fooaz
  1431. X
  1432. Xfoo\e\e+    foo\e foo\e\e foo\e\e\e  ...
  1433. X
  1434. X\e\|(foo\e)[1\-3]\e1    foo1foo foo2foo foo3foo
  1435. X(This is the same as \fIfoo[1\-3]foo\fP, but it takes less internal space.)
  1436. X
  1437. X\e\|(fo.*\e)\-\e1    foo\-foo fo\-fo fob\-fob foobar\-foobar ...
  1438. X.fi
  1439. X.RE
  1440. X.SH DIAGNOSTICS
  1441. X.I Re_comp
  1442. Xreturns one of the following strings if an error occurs:
  1443. X.RS
  1444. X.nf
  1445. X.I "No previous regular expression"
  1446. X.I "Empty closure"
  1447. X.I "Illegal closure"
  1448. X.I "Cyclical reference"
  1449. X.I "Undetermined reference"
  1450. X.I "Unmatched \e\|("
  1451. X.I "Missing ]"
  1452. X.I "Null pattern inside \e\|(\e)"
  1453. X.I "Null pattern inside \e<\e>"
  1454. X.I "Too many \e\|(\e) pairs"
  1455. X.I "Unmatched \e)"
  1456. X.fi
  1457. X.RE
  1458. X.SH REFERENCES
  1459. X.nf
  1460. X.IR "Software tools" ", Kernighan & Plauger."
  1461. X.IR "Software tools in Pascal" ", Kernighan & Plauger."
  1462. X.IR "Grep sources [rsx\-11 C dist]" ", David Conroy."
  1463. X.IR "Ed \- text editor" ", Unix Programmer's Manual."
  1464. X.IR "Advanced editing on Unix" ", B. W. Kernighan."
  1465. X.IR "RegExp sources" ", Henry Spencer."
  1466. X.fi
  1467. X.SH "HISTORY AND NOTES"
  1468. XThese routines are
  1469. X.IR Public Domain ,
  1470. Xyou can get them in source.
  1471. XThey are derived from various implementations found in the
  1472. X.I "Software Tools"
  1473. Xbooks, and David Conroy's
  1474. X.IR grep .
  1475. XThey are NOT derived from licensed/restricted software.
  1476. XFor more interesting/academic/complicated implementations, see Henry Spencer's
  1477. X.I regexp
  1478. Xroutines, or the
  1479. X.I "GNU Emacs"
  1480. Xpattern
  1481. Xmatching module.
  1482. X.PP
  1483. X.I Re_comp
  1484. Xand
  1485. X.I re_exec
  1486. Xgenerally perform at least as well as their licensed counterparts.
  1487. XIn a very few instances, they are about 10% to 15% slower.
  1488. X.SH AUTHOR
  1489. XOzan S. Yigit <yunexus!oz>.
  1490. X.br
  1491. XThis manual page was edited from the original by Rich $alz <rsalz@bbn.com>.
  1492. X.SH BUGS
  1493. XThe internal buffer for the compiled pattern is not checked for overflow;
  1494. Xthe size is currently 1024 bytes.
  1495. X.br
  1496. XThere are no doubt other bugs, too.
  1497. X.SH "SEE ALSO"
  1498. Xed(1), egrep(1), fgrep(1), grep(1)
  1499. END_OF_FILE
  1500.   if test 7421 -ne `wc -c <'regex.3'`; then
  1501.     echo shar: \"'regex.3'\" unpacked with wrong size!
  1502.   fi
  1503.   # end of 'regex.3'
  1504. fi
  1505. if test -f 'rfc822.c' -a "${1}" != "-c" ; then 
  1506.   echo shar: Will not clobber existing file \"'rfc822.c'\"
  1507. else
  1508.   echo shar: Extracting \"'rfc822.c'\" \(13942 characters\)
  1509.   sed "s/^X//" >'rfc822.c' <<'END_OF_FILE'
  1510. X/*
  1511. X**  Routines to read and write mail and news headers.  The code here
  1512. X**  is gross and complicated, and it would be nice if somebody rewrote
  1513. X**  it to be clean and simple, especially the CrackFrom routine.
  1514. X*/
  1515. X#include "gate.h"
  1516. X#include <time.h>
  1517. X#ifdef    RCSID
  1518. Xstatic char RCS[] =
  1519. X    "$Header: /nfs/papaya/u2/rsalz/src/newsgate/src/RCS/rfc822.c,v 1.5 91/02/12 14:54:00 rsalz Exp $";
  1520. X#endif    /* RCSID */
  1521. X
  1522. X#ifdef    HAVE_TIMEB
  1523. X#include <sys/timeb.h>
  1524. X#else
  1525. Xstruct timeb {
  1526. X    time_t        time;
  1527. X    unsigned short    millitm;
  1528. X    short        timezone;
  1529. X    short        dstflag;
  1530. X};
  1531. X#endif    /* HAVE_TIMEB */
  1532. X
  1533. Xextern time_t        time();
  1534. Xextern char        *asctime();
  1535. Xextern struct tm    *gmtime();
  1536. X
  1537. X#define HDR_OTHER        -1
  1538. X#define HDR_END            FALSE
  1539. X#define HDR_APPROVED         1
  1540. X#define HDR_CONTROL         2
  1541. X#define HDR_DATE         3
  1542. X#define HDR_DISTRIBUTION     4
  1543. X#define HDR_EXPIRE         5
  1544. X#define HDR_FOLLOWTO         6
  1545. X#define HDR_FROM          7
  1546. X#define HDR_KEYWORDS         8
  1547. X#define HDR_MESSAGEID         9
  1548. X#define HDR_NEWSGROUP         10
  1549. X#define HDR_ORGANIZATION    11
  1550. X#define HDR_REFERENCES        12
  1551. X#define HDR_REPLYTO        13
  1552. X#define HDR_SENDER        14
  1553. X#define HDR_SUMMARY        15
  1554. X#define HDR_TITLE         16
  1555. X
  1556. X
  1557. X/*
  1558. X**  The list of headers we recognize; all others are stripped.
  1559. X*/
  1560. Xtypedef struct _HTYPE {
  1561. X    char    *Name;
  1562. X    int        Type;
  1563. X} HTYPE;
  1564. X
  1565. XSTATIC HTYPE    HeaderList[] = {
  1566. X    {    "Approved:",        HDR_APPROVED        },
  1567. X    {    "Control:",        HDR_CONTROL        },
  1568. X    {    "Date:",        HDR_DATE        },
  1569. X    {    "Posted:",        HDR_DATE        },
  1570. X    {    "Distribution:",    HDR_DISTRIBUTION    },
  1571. X    {    "Expires:",        HDR_EXPIRE        },
  1572. X    {    "Followup-To:",        HDR_FOLLOWTO        },
  1573. X    {    "From:",        HDR_FROM        },
  1574. X    {    "Keywords:",        HDR_KEYWORDS        },
  1575. X    {    "Message-ID:",        HDR_MESSAGEID        },
  1576. X    {    "Newsgroups:",        HDR_NEWSGROUP        },
  1577. X    {    "Organization:",    HDR_ORGANIZATION    },
  1578. X    {    "In-Reply-To:",        HDR_REFERENCES        },
  1579. X    {    "References:",        HDR_REFERENCES        },
  1580. X    {    "Reply-To:",        HDR_REPLYTO        },
  1581. X    {    "Sender:",        HDR_SENDER        },
  1582. X    {    "Summary:",        HDR_SUMMARY        },
  1583. X    {    "Subject:",        HDR_TITLE        },
  1584. X    {    "Title:",        HDR_TITLE        },
  1585. X};
  1586. X
  1587. X
  1588. X
  1589. X/*
  1590. X**  Getline is like fgets, but deals with continuation lines.  It also
  1591. X**  ensures that even if a line that is too long is received, the
  1592. X**  remainder of the line is thrown away instead of treated like a second
  1593. X**  line.
  1594. X*/
  1595. XSTATIC char *
  1596. XGetline(buf, len, fp)
  1597. X    char        *buf;
  1598. X    int            len;
  1599. X    FILE        *fp;
  1600. X{
  1601. X    register char    *cp;
  1602. X    register int    c;
  1603. X    register int    n;
  1604. X
  1605. X    for (n = 0, cp = buf; n < len && (c = getc(fp)) != EOF && c != '\n'; )
  1606. X    if (!iscntrl(c) || c == '\b' || c == '\t') {
  1607. X        *cp++ = c;
  1608. X        n++;
  1609. X    }
  1610. X    if (c == EOF && cp == buf)
  1611. X    return NULL;
  1612. X    *cp = '\0';
  1613. X
  1614. X    if (c != '\n')
  1615. X    /* Line too long - part read didn't fit into a newline */
  1616. X    while ((c = getc(fp)) != '\n' && c != EOF)
  1617. X        ;
  1618. X    else if (cp == buf) {
  1619. X    /* Don't look for continuation of blank lines */
  1620. X    *cp++ = '\n';
  1621. X    *cp = '\0';
  1622. X    return buf;
  1623. X    }
  1624. X
  1625. X    while ((c = getc(fp)) == ' ' || c == '\t') {
  1626. X    /* Continuation line. */
  1627. X    if ((n += 2) < len) {
  1628. X        *cp++ = '\n';
  1629. X        *cp++ = c;
  1630. X    }
  1631. X    while ((c = getc(fp)) != '\n' && c != EOF)
  1632. X        if ((!iscntrl(c) || c == '\b' || c == '\t') && n++ < len)
  1633. X        *cp++ = c;
  1634. X    }
  1635. X    if (n >= len - 1)
  1636. X    cp = buf + len - 2;
  1637. X    *cp++ = '\n';
  1638. X    *cp = '\0';
  1639. X    if (c != EOF)
  1640. X    /* push back first char of next header */
  1641. X    (void)ungetc(c, fp);
  1642. X    return buf;
  1643. X}
  1644. X
  1645. X
  1646. X/*
  1647. X**  I guess this is basically strncasecmp
  1648. X*/
  1649. XSTATIC int
  1650. Xprefix(full, pref)
  1651. X    register char    *full;
  1652. X    register char    *pref;
  1653. X{
  1654. X    register char    fc;
  1655. X    register char    pc;
  1656. X
  1657. X    while (pc = *pref++) {
  1658. X    fc = *full++;
  1659. X    if (isupper(fc))
  1660. X        fc = tolower(fc);
  1661. X    if (isupper(pc))
  1662. X        pc = tolower(pc);
  1663. X    if (fc != pc)
  1664. X        return FALSE;
  1665. X    }
  1666. X    return TRUE;
  1667. X}
  1668. X
  1669. X
  1670. XSTATIC int
  1671. XHeaderType(p)
  1672. X    register char    *p;
  1673. X{
  1674. X    static int        save = HDR_END;
  1675. X    register HTYPE    *hp;
  1676. X    char        *colon;
  1677. X    char        *space;
  1678. X
  1679. X    /* some consistency checks (i.e. is this really a header line?) */
  1680. X    if (p == NULL || !isascii(*p) || *p == '\n')
  1681. X    return save = HDR_END;
  1682. X
  1683. X    if (WHITE(*p))
  1684. X    /* Continuation line. */
  1685. X    return save;
  1686. X
  1687. X    /* If we don't get a "<no space> <colon> <space>", it's not a header. */
  1688. X    if ((colon = IDX(p, ':')) == NULL
  1689. X     || ((space = IDX(p, ' ')) && space < colon))
  1690. X    return save = HDR_END;
  1691. X
  1692. X    for (hp = HeaderList; hp < ENDOF(HeaderList); hp++)
  1693. X    if (prefix(p, hp->Name))
  1694. X        return save = hp->Type;
  1695. X    return save = HDR_OTHER;
  1696. X}
  1697. X
  1698. X
  1699. X/*
  1700. X**  Get the contents of the field of the header line, appending it, with a
  1701. X**  space delimeter if it's a continuation line.  If there is already
  1702. X**  something in the header storage, skip this header line and the
  1703. X**  continuations.
  1704. X*/
  1705. XSTATIC void
  1706. Xgetfield(src, dest, size)
  1707. X    register char    *src;
  1708. X    register char    *dest;
  1709. X    register int    size;
  1710. X{
  1711. X    static int        skip = FALSE;
  1712. X    register char    *p;
  1713. X
  1714. X    if (src == NULL || dest == NULL)
  1715. X    return;
  1716. X
  1717. X    if (WHITE(*src)) {
  1718. X    /* Continuation line.  If skipping or no room, ignore. */
  1719. X    if (skip || (size -= strlen(dest)) <= 0)
  1720. X        return;
  1721. X    /* Munch all but one whitespace, append it to header. */
  1722. X    while (*src && WHITE(*src))
  1723. X        src++;
  1724. X    *--src = ' ';
  1725. X    (void)strncat(dest, src, size - 1);
  1726. X    }
  1727. X    else {
  1728. X    skip = FALSE;
  1729. X    if (*dest) {
  1730. X        /* Already got a value, so mark this as one to skip. */
  1731. X        skip = TRUE;
  1732. X        return;
  1733. X    }
  1734. X    if ((src = IDX(src, ':')) == NULL)
  1735. X        /* Can't happen! */
  1736. X        return;
  1737. X    /* Skip colon, eat whitespace. */
  1738. X    for (src++; *src && WHITE(*src); )
  1739. X        src++;
  1740. X    (void)strncpy(dest, src, size - 1);
  1741. X    }
  1742. X
  1743. X    /* Munch trailing whitespace. */
  1744. X    for (p = dest + strlen(dest); --p >= dest && (*p == '\n' || WHITE(*p)); )
  1745. X    ;
  1746. X    p[1] = '\0';
  1747. X}
  1748. X
  1749. X
  1750. XSTATIC time_t
  1751. Xcgtdate(datestr)
  1752. X    char        *datestr;
  1753. X{
  1754. X    static time_t    lasttime;
  1755. X    static char        save[SM_SIZE];
  1756. X    char        junk[40];
  1757. X    char        month[40];
  1758. X    char        day[30];
  1759. X    char        tod[60];
  1760. X    char        year[50];
  1761. X    char        buf[SM_SIZE];
  1762. X    extern time_t    getdate();
  1763. X
  1764. X    if (save[0] && EQ(datestr, save))
  1765. X    return lasttime;
  1766. X    lasttime = getdate(datestr, (struct timeb *)NULL);
  1767. X    if (lasttime < 0 &&
  1768. X      sscanf(datestr, "%s %s %s %s %s", junk, month, day, tod, year) == 5) {
  1769. X    (void)sprintf(buf, "%s %s, %s %s", month, day, year, tod);
  1770. X    lasttime = getdate(buf, (struct timeb *)NULL);
  1771. X    }
  1772. X    (void)strncpy(save, datestr, sizeof save);
  1773. X    return lasttime;
  1774. X}
  1775. X
  1776. X
  1777. X/*
  1778. X**  Print the date in ARPA format, not ctime(3) format.
  1779. X*/
  1780. XSTATIC void
  1781. XDoDate(fp, ud)
  1782. X    FILE        *fp;
  1783. X    register char    *ud;
  1784. X{
  1785. X    register char    *p;
  1786. X    register char    *q;
  1787. X    register int    i;
  1788. X    char        buff[SM_SIZE];
  1789. X
  1790. X    q = buff;
  1791. X
  1792. X#if    0
  1793. X    /* until every site installs the fix to getdate.y, the day
  1794. X     * of the week can cause time warps */
  1795. X    /* "Mon, " */
  1796. X    p = &ud[0]; *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = ','; *q++ = ' ';
  1797. X#endif    /* 0 */
  1798. X
  1799. X    /* "16" */
  1800. X    p = &ud[8];
  1801. X    if (*p == ' ')
  1802. X    p++;
  1803. X    else
  1804. X    *q++ = *p++;
  1805. X    *q++ = *p++; *q++ = ' ';
  1806. X
  1807. X    /* "Sep " */
  1808. X    p = &ud[4]; *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = ' ';
  1809. X
  1810. X    /* "79 " */
  1811. X    p = &ud[22]; *q++ = *p++; *q++ = *p++; *q++ = ' ';
  1812. X
  1813. X    /* "01:03:52" */
  1814. X    for (p = &ud[11], i = 8; i > 0; i--)
  1815. X    *q++ = *p++;
  1816. X
  1817. X    /* " GMT" */
  1818. X    *q++ = ' '; *q++ = 'G'; *q++ = 'M'; *q++ = 'T'; *q = '\0';
  1819. X
  1820. X    Fprintf(fp, "Date: %s\n", buff);
  1821. X}
  1822. X
  1823. X
  1824. X/*
  1825. X**  Strip leading and trailing spaces; returns pointer to first non-space.
  1826. X*/
  1827. XSTATIC char *
  1828. XStripSpaces(s)
  1829. X    register char    *s;
  1830. X{
  1831. X    register char    *cp;
  1832. X
  1833. X    if (s == NULL || *s == '\0')
  1834. X    return s;
  1835. X
  1836. X    /* Skip leading spaces. */
  1837. X    while (*s && isspace(*s))
  1838. X    s++;
  1839. X
  1840. X    /* zap trailing spaces */
  1841. X    for (cp = &s[strlen(s) - 1]; cp > s && isspace(*cp); )
  1842. X    cp--;
  1843. X    cp[1] = '\0';
  1844. X    return s;
  1845. X}
  1846. X
  1847. X
  1848. X/*
  1849. X**  Crack an RFC822 from header field into address and fullname.  We do
  1850. X**  this to make sure we write things out in official form.  "Be liberal
  1851. X**  in what you accept, conservative in what you generate."  Anyhow, we
  1852. X**  read things into three buffers, one for all <...> text, one for all
  1853. X**  (...) text, and a third for stuff not in either.  Either the first or
  1854. X**  third buffer will be the real address, depending on whether there is
  1855. X**  anything in buffer two or not.
  1856. X*/
  1857. Xint
  1858. XCrackFrom(addr, name, p)
  1859. X    char        *addr;
  1860. X    char        *name;
  1861. X    char        *p;
  1862. X{
  1863. X    register char    *ap;
  1864. X    register char    *cp;
  1865. X    register int    flag;
  1866. X    register int    comment;
  1867. X    register int    address;
  1868. X    register int    addrfound;
  1869. X    char        *comm;
  1870. X    char        commbuf[LG_SIZE];
  1871. X    char        addrbuf[LG_SIZE];
  1872. X
  1873. X    /* Just to make sure. */
  1874. X    *name = '\0';
  1875. X    *addr = '\0';
  1876. X
  1877. X    if (p == NULL)
  1878. X    return FALSE;
  1879. X
  1880. X    /* Eat leading white space. */
  1881. X    while (*p && isspace(*p))
  1882. X    p++;
  1883. X
  1884. X    /* Set defaults.  Start with an allocated copy of a comment string. */
  1885. X    comm = COPY("");
  1886. X    ap = addrbuf;
  1887. X    comment = 0;
  1888. X    addrfound = 0;
  1889. X    address = 0;
  1890. X    for (flag = 0; *p; p++) {
  1891. X    switch (*p) {
  1892. X    case '"':
  1893. X        if (flag) {
  1894. X        flag--;
  1895. X        goto EndComment;
  1896. X        }
  1897. X        flag++;
  1898. X        /* FALLTHROUGH */
  1899. X    case '(':
  1900. X        if (comment == 0) {
  1901. X        cp = commbuf;
  1902. X        *cp = '\0';
  1903. X        }
  1904. X        comment++;
  1905. X        break;
  1906. X    case ')':
  1907. X    EndComment:
  1908. X        if (comment > 0 && --comment == 0) {
  1909. X        if (*p != ')' && *p != '"')
  1910. X            *cp++ = *p;        /* Copy the comment-closer */
  1911. X        *cp = '\0';
  1912. X        if (*comm == '\0') {
  1913. X            free(comm);
  1914. X            comm = COPY(&commbuf[1]);
  1915. X        }
  1916. X        else {
  1917. X            cp = NEW(char, strlen(comm) + 2 + strlen(&commbuf[1]) + 1);
  1918. X            (void)sprintf(cp, "%s, %s", comm, &commbuf[1]);
  1919. X            free(comm);
  1920. X            comm = cp;
  1921. X        }
  1922. X        cp = NULL;
  1923. X        continue;
  1924. X        }
  1925. X        break;
  1926. X    case '<':
  1927. X        if (address) {
  1928. X        free(comm);
  1929. X        return FALSE;    /* AWK! Abort! */
  1930. X        }
  1931. X        if (!comment) {
  1932. X        address++;
  1933. X        *ap = '\0';
  1934. X        ap = addr;
  1935. X        }
  1936. X        break;
  1937. X    case '>':
  1938. X        if (!comment && address) {
  1939. X        address--;
  1940. X        addrfound++;
  1941. X        *ap = '\0';
  1942. X        ap = &addrbuf[strlen(addrbuf)];
  1943. X        p++;    /* skip the `>' */
  1944. X        }
  1945. X        break;
  1946. X    }
  1947. X
  1948. X    if (comment)
  1949. X        *cp++ = *p;
  1950. X    else if (!address || *p != '<')
  1951. X        *ap++ = *p;
  1952. X    if (*p == '\0')
  1953. X        break;
  1954. X    }
  1955. X
  1956. X    *ap++ = '\0';
  1957. X
  1958. X    if (addrfound) {
  1959. X    Strcpy(name, StripSpaces(addrbuf));
  1960. X    Strcpy(addr, strcpy(commbuf, StripSpaces(addr)));
  1961. X    }
  1962. X    else {
  1963. X    (void)strcpy(addr, StripSpaces(addrbuf));
  1964. X    *name = '\0';
  1965. X    }
  1966. X    /* Just to be sure that we got the full name, we'll take all of
  1967. X     * the comments. */
  1968. X    if (*comm) {
  1969. X    if (*name)
  1970. X        Strcat(name, ", ");
  1971. X    Strcat(name, comm);
  1972. X    }
  1973. X    free(comm);
  1974. X    return TRUE;
  1975. X}
  1976. X
  1977. X
  1978. X/*
  1979. X**  Write out an RFC822 header, paying no attention to line limits.
  1980. X**  Ideally, we should do continuations in here...
  1981. X*/
  1982. Xint
  1983. Xrfc822write(hp, fp)
  1984. X    register HBUF    *hp;
  1985. X    register FILE    *fp;
  1986. X{
  1987. X    time_t        t;
  1988. X
  1989. X    if (hp->path[0])
  1990. X    Fprintf(fp, "Path: %s\n", hp->path);
  1991. X    if (hp->from[0])
  1992. X    Fprintf(fp, "From: %s\n", hp->from);
  1993. X    if (hp->nbuf[0])
  1994. X    Fprintf(fp, "Newsgroups: %s\n", hp->nbuf);
  1995. X    if (hp->title[0])
  1996. X    Fprintf(fp, "Subject: %s\n", hp->title);
  1997. X    if (hp->ident[0])
  1998. X    Fprintf(fp, "Message-ID: %s\n", hp->ident);
  1999. X    /* Get current time. This will be used resolve the timezone. */
  2000. X    t = hp->subdate[0] ? cgtdate(hp->subdate) : time((time_t *)NULL);
  2001. X    DoDate(fp, asctime(gmtime(&t)));
  2002. X    if (hp->expdate[0])
  2003. X    Fprintf(fp, "Expires: %s\n", hp->expdate);
  2004. X    if (hp->followid[0])
  2005. X    Fprintf(fp, "References: %s\n", hp->followid);
  2006. X    if (hp->ctlmsg[0])
  2007. X    Fprintf(fp, "Control: %s\n", hp->ctlmsg);
  2008. X    if (hp->sender[0])
  2009. X    Fprintf(fp, "Sender: %s\n", hp->sender);
  2010. X    if (hp->replyto[0])
  2011. X    Fprintf(fp, "Reply-To: %s\n", hp->replyto);
  2012. X    if (hp->followto[0])
  2013. X    Fprintf(fp, "Followup-To: %s\n", hp->followto);
  2014. X    if (hp->distribution[0])
  2015. X    Fprintf(fp, "Distribution: %s\n", hp->distribution);
  2016. X    if (hp->organization[0])
  2017. X    Fprintf(fp, "Organization: %s\n", hp->organization);
  2018. X    if (hp->keywords[0])
  2019. X    Fprintf(fp, "Keywords: %s\n", hp->keywords);
  2020. X    if (hp->summary[0])
  2021. X    Fprintf(fp, "Summary: %s\n", hp->summary);
  2022. X    if (hp->approved[0])
  2023. X    Fprintf(fp, "Approved: %s\n", hp->approved);
  2024. X    Fprintf(fp, "\n");
  2025. X    return !ferror(fp);
  2026. X}
  2027. X
  2028. X
  2029. Xrfc822read(hp, fp, buff, buffsize)
  2030. X    register HBUF    *hp;
  2031. X    register FILE    *fp;
  2032. X    char        *buff;
  2033. X    int            buffsize;
  2034. X{
  2035. X    register int    i;
  2036. X    long        curpos;
  2037. X
  2038. X    /* Zap out the headers. */
  2039. X    hp->approved[0] = '\0';
  2040. X    hp->ctlmsg[0] = '\0';
  2041. X    hp->subdate[0] = '\0';
  2042. X    hp->distribution[0] = '\0';
  2043. X    hp->expdate[0] = '\0';
  2044. X    hp->followto[0] = '\0';
  2045. X    hp->from[0] = '\0';
  2046. X    hp->followid[0] = '\0';
  2047. X    hp->keywords[0] = '\0';
  2048. X    hp->ident[0] = '\0';
  2049. X    hp->nbuf[0] = '\0';
  2050. X    hp->organization[0] = '\0';
  2051. X    hp->title[0] = '\0';
  2052. X    hp->replyto[0] = '\0';
  2053. X    hp->summary[0] = '\0';
  2054. X    hp->path[0] = '\0';
  2055. X    hp->sender[0] = '\0';
  2056. X
  2057. X    i = HeaderType(buff);
  2058. X    do {
  2059. X    curpos = ftell(fp);
  2060. X    switch (i) {
  2061. X    case HDR_APPROVED:
  2062. X        getfield(buff, hp->approved, sizeof hp->approved);
  2063. X        break;
  2064. X    case HDR_CONTROL:
  2065. X        getfield(buff, hp->ctlmsg, sizeof hp->ctlmsg);
  2066. X        break;
  2067. X    case HDR_DATE:
  2068. X        getfield(buff, hp->subdate, sizeof hp->subdate);
  2069. X        break;
  2070. X    case HDR_DISTRIBUTION:
  2071. X        getfield(buff, hp->distribution, sizeof hp->distribution);
  2072. X        break;
  2073. X    case HDR_EXPIRE:
  2074. X        getfield(buff, hp->expdate, sizeof hp->expdate);
  2075. X        break;
  2076. X    case HDR_FOLLOWTO:
  2077. X        getfield(buff, hp->followto, sizeof hp->followto);
  2078. X        break;
  2079. X    case HDR_FROM:
  2080. X        getfield(buff, hp->from, sizeof hp->from);
  2081. X        break;
  2082. X    case HDR_KEYWORDS:
  2083. X        getfield(buff, hp->keywords, sizeof hp->keywords);
  2084. X        break;
  2085. X    case HDR_MESSAGEID:
  2086. X        getfield(buff, hp->ident, sizeof hp->ident);
  2087. X        break;
  2088. X    case HDR_NEWSGROUP:
  2089. X        getfield(buff, hp->nbuf, sizeof hp->nbuf);
  2090. X        break;
  2091. X    case HDR_ORGANIZATION:
  2092. X        getfield(buff, hp->organization, sizeof hp->organization);
  2093. X        break;
  2094. X    case HDR_REFERENCES:
  2095. X        getfield(buff, hp->followid, sizeof hp->followid);
  2096. X        break;
  2097. X    case HDR_REPLYTO:
  2098. X        getfield(buff, hp->replyto, sizeof hp->replyto);
  2099. X        break;
  2100. X    case HDR_SENDER:
  2101. X        getfield(buff, hp->sender, sizeof hp->sender);
  2102. X        break;
  2103. X    case HDR_SUMMARY:
  2104. X        getfield(buff, hp->summary, sizeof hp->summary);
  2105. X        break;
  2106. X    case HDR_TITLE:
  2107. X        getfield(buff, hp->title, sizeof hp->title);
  2108. X        break;
  2109. X    }
  2110. X    } while ((i = HeaderType(Getline(buff, buffsize, fp))) != HDR_END);
  2111. X
  2112. X    if (*buff != '\n')
  2113. X    (void)fseek(fp, curpos, 0);
  2114. X}
  2115. END_OF_FILE
  2116.   if test 13942 -ne `wc -c <'rfc822.c'`; then
  2117.     echo shar: \"'rfc822.c'\" unpacked with wrong size!
  2118.   fi
  2119.   # end of 'rfc822.c'
  2120. fi
  2121. echo shar: End of archive 2 \(of 4\).
  2122. cp /dev/null ark2isdone
  2123. MISSING=""
  2124. for I in 1 2 3 4 ; do
  2125.     if test ! -f ark${I}isdone ; then
  2126.     MISSING="${MISSING} ${I}"
  2127.     fi
  2128. done
  2129. if test "${MISSING}" = "" ; then
  2130.     echo You have unpacked all 4 archives.
  2131.     rm -f ark[1-9]isdone
  2132. else
  2133.     echo You still must unpack the following archives:
  2134.     echo "        " ${MISSING}
  2135. fi
  2136. exit 0
  2137. exit 0 # Just in case...
  2138.